﻿using System;
using System.Diagnostics;

namespace Sencha.Serialization
{
	interface IStrongBox : IFormattable
	{
		byte AsByte { get; }
		short AsInt16 { get; }
		int AsInt32 { get; }
		long AsInt64 { get; }
		sbyte AsSByte { get; }
		ushort AsUInt16 { get; }
		uint AsUInt32 { get; }
		ulong AsUInt64 { get; }
		float AsSingle { get; }
		double AsDouble { get; }
		decimal AsDecimal { get; }
		Type ValueType { get; }
		object AsObject { get; }

		T Load<T>();
#if NO_TYPE_REFS
		void Store<T>(T value);
#else
		void Store(TypedReference value);
#endif
	}

	[DebuggerDisplay("<{ValueType}>{AsObject}")]
	sealed class StrongBox<ValueT> : IStrongBox where ValueT : struct
	{
		private static readonly Type ValueType = typeof(ValueT);

		public ValueT Value;

		public byte AsByte
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToByte((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return __refvalue( __makeref(this.Value),byte);
					}
					else if (Value is SByte)
					{
						return (byte) __refvalue( __makeref(this.Value),sbyte);
					}
					else if (Value is Int16)
					{
						return (byte) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (byte) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (byte) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (byte) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (byte) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (byte) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (byte) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (byte) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (byte) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<byte>();
				}
#endif
			}
		}
		public short AsInt16
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToInt16((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (short) __refvalue( __makeref(this.Value),byte);
					}
					else if (Value is SByte)
					{
						return (short) __refvalue( __makeref(this.Value),sbyte);
					}
					else if (Value is Int16)
					{
						return (short) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (short) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (short) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (short) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (short) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (short) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (short) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (short) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (short) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<short>();
				}
#endif
			}
		}
		public int AsInt32
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToInt32((object)Value);
#else
				checked
				{

					if (Value is Byte)
					{
						return (int) __refvalue( __makeref(this.Value),byte);
					}
					else if (Value is SByte)
					{
						return (int) __refvalue( __makeref(this.Value),sbyte);
					}
					else if (Value is Int16)
					{
						return (int) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (int) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (int) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (int) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (int) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (int) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (int) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (int) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (int) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<int>();
				}
#endif
			}
		}
		public long AsInt64
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToInt64((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (long) __refvalue( __makeref(this.Value),byte);
					}
					else if (Value is SByte)
					{
						return (long) __refvalue( __makeref(this.Value),sbyte);
					}
					else if (Value is Int16)
					{
						return (long) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (long) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (long) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (long) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (long) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (long) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (long) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (long) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (long) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<long>();
				}
#endif
			}
		}
		public sbyte AsSByte
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToSByte((object)Value);
#else
				checked
				{

					if (Value is Byte)
					{
						return (sbyte) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (sbyte) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (sbyte) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (sbyte) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (sbyte) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (sbyte) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<sbyte>();
				}
#endif
			}
		}
		public ushort AsUInt16
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToUInt16((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (ushort) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (ushort) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (ushort) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (ushort) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (ushort) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (ushort) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (ushort) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (ushort) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (ushort) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (ushort) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (ushort) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<ushort>();
				}
#endif
			}
		}
		public uint AsUInt32
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToUInt32((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (uint) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (uint) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (uint) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (uint) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (uint) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (uint) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (uint) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (uint) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (uint) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (uint) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (uint) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<uint>();
				}
#endif
			}
		}
		public ulong AsUInt64
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToUInt64((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (ulong) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (ulong) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (ulong) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (ulong) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (ulong) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (ulong) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (ulong) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (ulong) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (ulong) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (ulong) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (ulong) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<ulong>();
				}
#endif
			}
		}
		public float AsSingle
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToSingle((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (float) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (float) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (float) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (float) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (float) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (float) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (float) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (float) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (float) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (float) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (float) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<float>();
				}
#endif
			}
		}
		public double AsDouble
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToDouble((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (double) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (double) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (double) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (double) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (double) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (double) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (double) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (double) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (double) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (double) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (double) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<double>();
				}
#endif
			}
		}
		public decimal AsDecimal
		{
			get
			{
#if NO_TYPE_REFS
				return Convert.ToDecimal((object)Value);
#else
				checked
				{
					if (Value is Byte)
					{
						return (decimal) __refvalue( __makeref(this.Value),Byte);
					}
					else if (Value is SByte)
					{
						return (decimal) __refvalue( __makeref(this.Value),SByte);
					}
					else if (Value is Int16)
					{
						return (decimal) __refvalue(__makeref(this.Value),Int16);
					}
					else if (Value is Int32)
					{
						return (decimal) __refvalue(__makeref(this.Value),Int32);
					}
					else if (Value is Int64)
					{
						return (decimal) __refvalue(__makeref(this.Value),Int64);
					}
					else if (Value is UInt16)
					{
						return (decimal) __refvalue(__makeref(this.Value),UInt16);
					}
					else if (Value is UInt32)
					{
						return (decimal) __refvalue(__makeref(this.Value),UInt32);
					}
					else if (Value is UInt64)
					{
						return (decimal) __refvalue(__makeref(this.Value),UInt64);
					}
					else if (Value is Single)
					{
						return (decimal) __refvalue(__makeref(this.Value),Single);
					}
					else if (Value is Double)
					{
						return (decimal) __refvalue(__makeref(this.Value),Double);
					}
					else if (Value is Decimal)
					{
						return (decimal) __refvalue(__makeref(this.Value),Decimal);
					}
					else
						return Load<decimal>();
				}
#endif
			}
		}
		Type IStrongBox.ValueType { get { return ValueType; } }
		public object AsObject { get { return (object)this.Value; } }

		public T Load<T>()
		{
			var valueT = ValueType;
			var resultT = typeof(T);

#if NO_TYPE_REFS
			if (valueT == resultT)
				return (T)this.AsObject;
#else
			if (valueT == resultT)
				return __refvalue( __makeref(this.Value),T);
#endif
			else
				return TypeConvert.Convert<ValueT, T>(this.Value, null);
		}

#if NO_TYPE_REFS
		public void Store<T>(T value)
		{
			var type = typeof(T);
			if (type != ValueType)
				throw new ArgumentException(string.Format("Unable to store value of type '{0}' in container of type '{1}'", typeof(ValueT), type));
			this.Value = (ValueT)(object)value;
		}
#else
		public void Store(TypedReference value)
		{
			var type = TypedReference.GetTargetType(value);
			if (type != ValueType)
				throw new ArgumentException(string.Format("Unable to store value of type '{0}' in container of type '{1}'", typeof(ValueT), type));
			this.Value = __refvalue( value,ValueT);
		}
#endif

		public string ToString(string str, IFormatProvider format)
		{
			if (this.Value is IFormattable)
				return ((IFormattable)this.Value).ToString(str, format);
			return Convert.ToString(this.Value, format);
		}
		public override string ToString()
		{
			return this.ToString(null, null);
		}
	}
}
